use derive_builder::Builder;

use serde::{Deserialize, Serialize};

use crate::{
    error::Error,
    modeling::{ComparsionOperator, LogicalOperator},
};

use super::{
    AttributeCondition, AttributeConditionBuilder, ClassRelationCondition, OrderItem,
    QueryConditionExpression, QueryConditionExpressionBuilder, QuerySelect,
};

#[derive(Debug, Default, Serialize, Deserialize, Clone, Builder, PartialEq)]
#[builder(name = "QueryConditionBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "Error"))]
pub struct QueryCondition {
    pub selects: Option<Vec<QuerySelect>>,
    pub attributes: Option<Vec<AttributeCondition>>,
    pub relations: Option<Vec<ClassRelationCondition>>,
    pub expression: Option<QueryConditionExpression>,
    pub orders: Option<Vec<OrderItem>>,
    pub page_size: Option<u32>,
    pub page: Option<u32>,
}

impl QueryCondition {
    pub fn with_attribute(name: String, value: String) -> anyhow::Result<QueryCondition> {
        let res = Self {
            selects: None,
            attributes: Some(vec![AttributeCondition::new_with_equal(name, value)]),
            relations: None,
            orders: None,
            expression: None,
            page_size: None,
            page: None,
        };
        Ok(res)
    }

    pub fn with_relation(relation: ClassRelationCondition) -> anyhow::Result<QueryCondition> {
        let res = Self {
            selects: None,
            attributes: None,
            relations: Some(vec![relation]),
            expression: None,
            orders: None,
            page_size: None,
            page: None,
        };
        Ok(res)
    }

    pub fn with_relations(
        relations: Vec<ClassRelationCondition>,
    ) -> anyhow::Result<QueryCondition> {
        let res = Self {
            selects: None,
            attributes: None,
            relations: Some(relations),
            expression: None,
            orders: None,
            page_size: None,
            page: None,
        };
        Ok(res)
    }
}

impl QueryCondition {
    pub fn set_attribute_values(
        &mut self,
        name: String,
        operator: ComparsionOperator,
        values: Vec<String>,
    ) -> anyhow::Result<()> {
        if self.attributes.is_none() {
            self.attributes = Some(vec![AttributeConditionBuilder::default()
                .name(&name)
                .operator(operator)
                .values(values)
                .build()?]);
        } else {
            self.attributes
                .as_mut()
                .map_or(Err(anyhow::anyhow!("Option get失败")), |v| (Ok(v)))?
                .push(
                    AttributeConditionBuilder::default()
                        .name(&name)
                        .operator(operator)
                        .values(values)
                        .build()?,
                );
        }
        Ok(())
    }

    pub fn set_attribute(&mut self, name: String, value: String) -> anyhow::Result<()> {
        if self.attributes.is_none() {
            self.attributes = Some(vec![AttributeCondition::new_with_equal(name, value)]);
        } else {
            self.attributes
                .as_mut()
                .map_or(Err(anyhow::anyhow!("Option get失败")), |v| (Ok(v)))?
                .push(AttributeCondition::new_with_equal(name, value));
        }
        Ok(())
    }

    pub fn set_expression(
        &mut self,
        expression: QueryConditionExpression,
        operator: LogicalOperator,
    ) -> anyhow::Result<()> {
        if let Some(left_expression) = &self.expression {
            let left = left_expression;
            self.expression = Some(
                QueryConditionExpressionBuilder::default()
                    .operator(operator)
                    .left_expression(left.clone())
                    .right_expression(expression)
                    .build()?,
            );
        } else {
            self.expression = Some(expression);
        }

        Ok(())
    }

    pub fn set_page(&mut self, page: u32, page_size: u32) -> anyhow::Result<()> {
        self.page = Some(page);
        self.page_size = Some(page_size);
        Ok(())
    }
}

impl QueryCondition {
    pub fn combine(&mut self, condition: &QueryCondition) -> anyhow::Result<()> {
        //selects
        if let Some(items) = &condition.selects {
            if let Some(self_items) = &self.selects {
                //
                let mut new_items = self_items.clone();
                new_items.append(&mut items.clone());
                self.selects = Some(new_items);
            } else {
                self.selects = Some(items.clone());
            }
        }
        //attributes
        if let Some(items) = &condition.attributes {
            if let Some(self_items) = &self.attributes {
                //
                let mut new_items = self_items.clone();
                new_items.append(&mut items.clone());
                self.attributes = Some(new_items);
            } else {
                self.attributes = Some(items.clone());
            }
        }
        //relations
        if let Some(items) = &condition.relations {
            if let Some(self_items) = &self.relations {
                //
                let mut new_items = self_items.clone();
                new_items.append(&mut items.clone());
                self.relations = Some(new_items);
            } else {
                self.relations = Some(items.clone());
            }
        }
        //orders
        if let Some(items) = &condition.orders {
            if let Some(self_items) = &self.orders {
                //
                let mut new_items = self_items.clone();
                new_items.append(&mut items.clone());
                self.orders = Some(new_items);
            } else {
                self.orders = Some(items.clone());
            }
        }
        //page_size

        //page

        Ok(())
    }

    pub fn merge(&mut self, conditions: &Vec<QueryCondition>) -> anyhow::Result<()> {
        //
        for condition in conditions {
            self.combine(condition)?;
        }
        Ok(())
    }
}
